package com.jde.redis.core;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.jde.redis.constant.RedisDbIndex;
import com.jde.redis.utils.SpringBeanUtil;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName RedisProvider
 * @Description redis 操作封装类
 * @Author jiande
 * @Date 2020/7/2 9:20
 * @Version 1.0
 */

public class RedisProvider {

    /**
     * 获取RedisTemplate
     *
     * @return
     */
    private static RedisTemplate<String, String> template() {
        return (RedisTemplate<String, String>) SpringBeanUtil.getBean("redisTemplate");
    }

    /**
     * 把数据存到内存
     *
     * @param key
     * @param value
     * @return
     */
    public static boolean set(String key, Object value, long validTime, RedisDbIndex index) {
        Gson gson = new Gson();
        return trueSet(getKey(index, key), gson.toJson(value), validTime);
    }

    /**
     * 不设置过期时间
     *
     * @param key
     * @param value
     * @param index
     * @return
     */
    public static boolean set(String key, Object value, RedisDbIndex index) {
        Gson gson = new Gson();
        return trueSet(getKey(index, key), gson.toJson(value), -1);
    }

    /**
     * 获取数据转换成javabean
     *
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T get(String key, Class<T> clazz, RedisDbIndex index) {
        Gson gson = new Gson();
        return gson.fromJson(get(getKey(index, key)), clazz);
    }

    /**
     * 获取list对象
     *
     * @param key
     * @param typeToken
     * @param index
     * @param <T>
     * @return
     */
    public static <T> List<T> getList(String key, TypeToken<List<T>> typeToken, RedisDbIndex index) {
        Gson gson = new Gson();
        return gson.fromJson(get(index + key), typeToken.getType());
    }


    /**
     * 删除数据
     *
     * @param key
     * @return
     */
    public static boolean delete(String key, RedisDbIndex index) {
        RedisTemplate<String, String> template = template();
        return template.delete(getKey(index, key));
    }


    public static Long leftPush(String key, String value, RedisDbIndex index) {
        RedisTemplate<String, String> template = template();
        return template.opsForList().leftPush(getKey(index, key), value);
    }

    public static Long leftPush(String key, String value, long time, RedisDbIndex index) {
        RedisTemplate<String, String> template = template();
        template.expire(getKey(index, key), time, TimeUnit.SECONDS);
        return template.opsForList().leftPush(getKey(index, key), value);
    }

    public Long leftPushAll(String key, RedisDbIndex index, Object... values) {
        RedisTemplate<String, String> template = template();
        Gson gson = new Gson();
        return template.opsForList().leftPushAll(getKey(index, key), gson.toJson(values));
    }

    public Long leftPushAll(String key, Long time, RedisDbIndex index, Object... values) {
        RedisTemplate<String, String> template = template();
        Gson gson = new Gson();
        Long count = template.opsForList().leftPushAll(getKey(index, key), gson.toJson(values));
        template.expire(key, time, TimeUnit.SECONDS);
        return count;
    }

    /**
     * LTRIM操作
     * <p>
     * 对一个列表进行修剪(trim)，就是说，让列表只保留指定区间内的元素，不在指定区间之内的元素都将被删除。
     * <p>
     * 举个例子，执行命令 LTRIM list 0 2 ，表示只保留列表 list 的前三个元素，其余元素全部删除。
     * <p>
     * 下标(index)参数 start 和 stop 都以 0 为底，也就是说，以 0 表示列表的第一个元素，以 1 表示列表的第二个元素，以此类推。
     * <p>
     * 你也可以使用负数下标，以 -1 表示列表的最后一个元素， -2 表示列表的倒数第二个元素，以此类推。
     */
    public static void trim(String key, long start, long end, RedisDbIndex index) {
        RedisTemplate<String, String> template = template();
        template.opsForList().trim(getKey(index, key), start, end);
    }

    /**
     * 根据参数 count 的值，移除列表中与参数 value 相等的元素。
     * <p>
     * count 的值可以是以下几种：
     * <p>
     * count > 0 : 从表头开始向表尾搜索，移除与 value 相等的元素，数量为 count 。
     * count < 0 : 从表尾开始向表头搜索，移除与 value 相等的元素，数量为 count 的绝对值。
     * count = 0 : 移除表中所有与 value 相等的值。
     * 返回值：
     * 被移除元素的数量。
     * 因为不存在的 key 被视作空表(empty list)，所以当 key 不存在时， LREM 命令总是返回 0 。
     */
    public static Long remove(String key, long count, Object value, RedisDbIndex index) {
        RedisTemplate<String, String> template = template();
        return template.opsForList().remove(getKey(index, key), count, value);
    }


    public static List<String> range(String key, final long start, final long end, RedisDbIndex index) {
        RedisTemplate<String, String> template = template();
        return template.opsForList().range(getKey(index, key), start, end);
    }


    /**
     * 获取数据
     *
     * @param key
     * @return
     */
    private static String get(String key) {
        RedisTemplate<String, String> template = template();
        String result = template.execute((RedisCallback<String>) (connection) -> {
            RedisSerializer<String> serializer = template.getStringSerializer();
            byte[] value = connection.get(serializer.serialize(key));
            return serializer.deserialize(value);
        });
        return result;
    }

    /**
     * 把数据存到内存设置过期时间
     *
     * @param key
     * @param value
     * @param validTime
     * @return
     */
    private static boolean trueSet(String key, String value, long validTime) {
        RedisTemplate<String, String> template = template();
        boolean result = template.execute((RedisCallback<Boolean>) (connection) -> {
            RedisSerializer<String> serializer = template.getStringSerializer();
            connection.set(serializer.serialize(key), serializer.serialize(value));
            if (validTime != -1) {
                connection.expire(serializer.serialize(key), validTime);
            }
            return true;
        });
        return result;
    }

    private static String getKey(RedisDbIndex index, String key) {
        return index.key + key;
    }

}
